/*******************************************************************\
| Matrix4.h
|
|--------------------------------------------------------------------
| Copyright 2005. Martin Fleisz.
| All rights reserved.
|
|--------------------------------------------------------------------
| CREATED:		2006/2/21
| AUTHOR:		Martin Fleisz
|
|--------------------------------------------------------------------
| DESCRIPTION:
| 
| 4x4 Matrix class
\********************************************************************/
#ifndef MATRIX4_H
#define MATRIX4_H

#include "Vector3.h"
#include "Vector4.h"

// enable this define to pre-transpose the matrix class' memory layout (default: row-major)
//#define USE_PRETRANSPOSED_MATRIX

namespace Math
{


class Matrix4
{
		// matrix data
	public:
		union
		{
			struct
			{
#if !defined(USE_PRETRANSPOSED_MATRIX)
				float m_M11; float m_M12; float m_M13; float m_M14;
				float m_M21; float m_M22; float m_M23; float m_M24;
				float m_M31; float m_M32; float m_M33; float m_M34;
				float m_M41; float m_M42; float m_M43; float m_M44;
#else
				// the matrix is pretransposed for the OpenGL API!
				float m_M11; float m_M21; float m_M31; float m_M41;
				float m_M12; float m_M22; float m_M32; float m_M42;
				float m_M13; float m_M23; float m_M33; float m_M43;
				float m_M14; float m_M24; float m_M34; float m_M44;
#endif
			};
			float m_MatE[4][4];
			float m_Mat[16];
		};


		// constructors
	public:
		inline Matrix4() { }
		explicit inline Matrix4(const float* p_Mat);
		inline Matrix4(const Matrix4& p_Mat);

		// create identity matrix
		explicit inline Matrix4(const float p_Value);

		// 3x3 matrix construction
        inline Matrix4(float p_M11, float p_M12, float p_M13, 
						float p_M21, float p_M22, float p_M23, 
						float p_M31, float p_M32, float p_M33);

		// 4x4 matrix construction
		inline Matrix4(float p_M11, float p_M12, float p_M13, float p_M14,
						float p_M21, float p_M22, float p_M23, float p_M24, 
						float p_M31, float p_M32, float p_M33, float p_M34,
						float p_M41, float p_M42, float p_M43, float p_M44);
        
		// operators
	public:
		// +/- operator (matrix sign)
		inline Matrix4		operator+() const;
		inline Matrix4		operator-() const;

		// +=, -=, *=, /= operators
		inline Matrix4&	operator+=(const Matrix4& p_Mat);
		inline Matrix4&	operator-=(const Matrix4& p_Mat);
		inline Matrix4&	operator*=(const Matrix4& p_Mat);
		inline Matrix4&	operator*=(const float p_Value);
		inline Matrix4&	operator/=(const float p_Value);

		// matrix addition, substraction and dot product
		inline friend const Matrix4	operator+(const Matrix4& p_Mat1, const Matrix4& p_Mat2);
		inline friend const Matrix4	operator-(const Matrix4& p_Mat1, const Matrix4& p_Mat2);
		inline friend const Matrix4	operator*(const Matrix4& p_Mat, const float p_Value);
		inline friend const Matrix4	operator*(const float p_Value, const Matrix4& p_Mat);
		inline friend const Matrix4	operator*(const Matrix4& p_Mat1, const Matrix4& p_Mat2);
		inline friend const Matrix4	operator/(const Matrix4& p_Mat, const float p_Value);
		inline friend const Matrix4	operator/(const float p_Value, const Matrix4& p_Mat);

		// multiply matrix with vector
		inline friend Vector3			operator*=(Vector3& p_Vec, const Matrix4& p_Mat);
		inline friend Vector4			operator*=(Vector4& p_Vec, const Matrix4& p_Mat);

		inline friend const Vector3	operator*(const Matrix4& p_Mat, const Vector3& p_Vec);
		inline friend const Vector3	operator*(const Vector3& p_Vec, const Matrix4& p_Mat);
		inline friend const Vector4	operator*(const Matrix4& p_Mat, const Vector4& p_Vec);
		inline friend const Vector4	operator*(const Vector4& p_Vec, const Matrix4& p_Mat);

		// assignment operators
		inline Matrix4&	operator=(const float p_Value);
		inline Matrix4&	operator=(const Matrix4& p_Mat);

	public:

		// make identity matrix
		inline void				Identity();

		// invert the matrix
		void					Invert();

		// transposes the matrix
		void					Transpose();

		// invert the transformation matrix
		void					InvertTransformationMatrix();

		// rotate the vector with our rotation matrix
        inline const Vector3	RotateOnly(const Vector3& p_Vec);

		// get/set methods
		inline const Vector4	GetCol(uint32 p_Col);
		inline void				SetCol(uint32 p_Col, const Vector4& p_Vec);

		inline const Vector4	GetRow(uint32 p_Row);
		inline void				SetRow(uint32 p_Row, const Vector4& p_Vec);

		// create a perspective projection matrix
		inline void				PerspectiveMatrix(const float p_Fov, const float p_Aspect, const float p_Near, const float p_Far);

		// create an orthogonal projection matrix
		inline void				OrthoMatrix(const float p_Left, const float p_Top, const float p_Right, const float p_Bottom, const float p_Near, const float p_Far);

	public:
		// special predefined values
		static const Matrix4 ZERO;
		static const Matrix4 IDENTITY;
};

/* ***************************************************************** *\
|  INLINE FUNCTIONS
\* ***************************************************************** */
inline Matrix4::Matrix4(const float* p_Mat)
{
    for(uint32 i = 0; i < 16; i++)
		m_Mat[i] = p_Mat[i];
}

inline Matrix4::Matrix4(const Matrix4& p_Mat)
{
	m_M11 = p_Mat.m_M11;	m_M12 = p_Mat.m_M12;	m_M13 = p_Mat.m_M13;	m_M14 = p_Mat.m_M14;
	m_M21 = p_Mat.m_M21;	m_M22 = p_Mat.m_M22;	m_M23 = p_Mat.m_M23;	m_M24 = p_Mat.m_M24;
	m_M31 = p_Mat.m_M31;	m_M32 = p_Mat.m_M32;	m_M33 = p_Mat.m_M33;	m_M34 = p_Mat.m_M34;
	m_M41 = p_Mat.m_M41;	m_M42 = p_Mat.m_M42;	m_M43 = p_Mat.m_M43;	m_M44 = p_Mat.m_M44;
}

inline Matrix4::Matrix4(const float p_Value)
{
	m_M11 = p_Value;	m_M12 = 0.0f;		m_M13 = 0.0f;		m_M14 = 0.0f;
	m_M21 = 0.0f;		m_M22 = p_Value;	m_M23 = 0.0f;		m_M24 = 0.0f;
	m_M31 = 0.0f;		m_M32 = 0.0f;		m_M33 = p_Value;	m_M34 = 0.0f;
	m_M41 = 0.0f;		m_M42 = 0.0f;		m_M43 = 0.0f;		m_M44 = p_Value;
}

inline Matrix4::Matrix4(float p_M11, float p_M12, float p_M13, 
						  float p_M21, float p_M22, float p_M23, 
						  float p_M31, float p_M32, float p_M33)
{
	m_M11 = p_M11;	m_M12 = p_M12;	m_M13 = p_M13;	m_M14 = 0.0f;
	m_M21 = p_M21;	m_M22 = p_M22;	m_M23 = p_M23;	m_M24 = 0.0f;
	m_M31 = p_M31;	m_M32 = p_M32;	m_M33 = p_M33;	m_M34 = 0.0f;
	m_M41 = 0.0f;	m_M42 = 0.0f;	m_M43 = 0.0f;	m_M44 = 1.0f;
}

inline Matrix4::Matrix4(float p_M11, float p_M12, float p_M13, float p_M14,
						  float p_M21, float p_M22, float p_M23, float p_M24, 
						  float p_M31, float p_M32, float p_M33, float p_M34,
						  float p_M41, float p_M42, float p_M43, float p_M44)
{
	m_M11 = p_M11;	m_M12 = p_M12;	m_M13 = p_M13;	m_M14 = p_M14;
	m_M21 = p_M21;	m_M22 = p_M22;	m_M23 = p_M23;	m_M24 = p_M24;
	m_M31 = p_M31;	m_M32 = p_M32;	m_M33 = p_M33;	m_M34 = p_M34;
	m_M41 = p_M41;	m_M42 = p_M42;	m_M43 = p_M43;	m_M44 = p_M44;
}

// +/- sign operators
inline Matrix4 Matrix4::operator+() const
{
	return (*this);
}

inline Matrix4 Matrix4::operator-() const
{
	return Matrix4(-m_M11, -m_M12, -m_M13, -m_M14,
					-m_M21, -m_M22, -m_M23, -m_M24,
					-m_M31, -m_M32, -m_M33, -m_M34,
					-m_M41, -m_M42, -m_M43, -m_M44);
}

// += operator
inline Matrix4& Matrix4::operator+=(const Matrix4& p_Mat)
{
	m_M11 += p_Mat.m_M11;	m_M12 += p_Mat.m_M12;	m_M13 += p_Mat.m_M13;	m_M14 += p_Mat.m_M14;
	m_M21 += p_Mat.m_M21;	m_M22 += p_Mat.m_M22;	m_M23 += p_Mat.m_M23;	m_M24 += p_Mat.m_M24;
	m_M31 += p_Mat.m_M31;	m_M32 += p_Mat.m_M32;	m_M33 += p_Mat.m_M33;	m_M34 += p_Mat.m_M34;
	m_M41 += p_Mat.m_M41;	m_M42 += p_Mat.m_M42;	m_M43 += p_Mat.m_M43;	m_M44 += p_Mat.m_M44;
	
	return (*this);
}

// -= operator
inline Matrix4& Matrix4::operator-=(const Matrix4& p_Mat)
{
	m_M11 -= p_Mat.m_M11;	m_M12 -= p_Mat.m_M12;	m_M13 -= p_Mat.m_M13;	m_M14 -= p_Mat.m_M14;
	m_M21 -= p_Mat.m_M21;	m_M22 -= p_Mat.m_M22;	m_M23 -= p_Mat.m_M23;	m_M24 -= p_Mat.m_M24;
	m_M31 -= p_Mat.m_M31;	m_M32 -= p_Mat.m_M32;	m_M33 -= p_Mat.m_M33;	m_M34 -= p_Mat.m_M34;
	m_M41 -= p_Mat.m_M41;	m_M42 -= p_Mat.m_M42;	m_M43 -= p_Mat.m_M43;	m_M44 -= p_Mat.m_M44;

	return (*this);
}

// scalar multiplication
inline Matrix4& Matrix4::operator*=(const float p_Value)
{
	m_M11 *= p_Value;	m_M12 *= p_Value;	m_M13 *= p_Value;	m_M14 *= p_Value;
	m_M21 *= p_Value;	m_M22 *= p_Value;	m_M23 *= p_Value;	m_M24 *= p_Value;
	m_M31 *= p_Value;	m_M32 *= p_Value;	m_M33 *= p_Value;	m_M34 *= p_Value;
	m_M41 *= p_Value;	m_M42 *= p_Value;	m_M43 *= p_Value;	m_M44 *= p_Value;

	return (*this);
}

// scalar division
inline Matrix4& Matrix4::operator/=(const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	m_M11 *= invValue;	m_M12 *= invValue;	m_M13 *= invValue;	m_M14 *= invValue;
	m_M21 *= invValue;	m_M22 *= invValue;	m_M23 *= invValue;	m_M24 *= invValue;
	m_M31 *= invValue;	m_M32 *= invValue;	m_M33 *= invValue;	m_M34 *= invValue;
	m_M41 *= invValue;	m_M42 *= invValue;	m_M43 *= invValue;	m_M44 *= invValue;

	return (*this);
}

// matrix multiplication (self)
inline Matrix4& Matrix4::operator*=(const Matrix4& p_Mat)
{
	return ((*this) = (*this) * p_Mat);
}

// matrix addition
inline const Matrix4 operator+(const Matrix4& p_Mat1,const Matrix4& p_Mat2)
{
	return Matrix4(
		p_Mat1.m_M11 + p_Mat2.m_M11, p_Mat1.m_M12 + p_Mat2.m_M12, p_Mat1.m_M13 + p_Mat2.m_M13, p_Mat1.m_M14 + p_Mat2.m_M14,
		p_Mat1.m_M21 + p_Mat2.m_M21, p_Mat1.m_M22 + p_Mat2.m_M22, p_Mat1.m_M23 + p_Mat2.m_M23, p_Mat1.m_M24 + p_Mat2.m_M24,
		p_Mat1.m_M31 + p_Mat2.m_M31, p_Mat1.m_M32 + p_Mat2.m_M32, p_Mat1.m_M33 + p_Mat2.m_M33, p_Mat1.m_M34 + p_Mat2.m_M34,
		p_Mat1.m_M41 + p_Mat2.m_M41, p_Mat1.m_M42 + p_Mat2.m_M42, p_Mat1.m_M43 + p_Mat2.m_M43, p_Mat1.m_M44 + p_Mat2.m_M44);
}

// matrix subtraction
inline const Matrix4 operator-(const Matrix4& p_Mat1,const Matrix4& p_Mat2)
{
	return Matrix4(
		p_Mat1.m_M11 - p_Mat2.m_M11, p_Mat1.m_M12 - p_Mat2.m_M12, p_Mat1.m_M13 - p_Mat2.m_M13, p_Mat1.m_M14 - p_Mat2.m_M14,
		p_Mat1.m_M21 - p_Mat2.m_M21, p_Mat1.m_M22 - p_Mat2.m_M22, p_Mat1.m_M23 - p_Mat2.m_M23, p_Mat1.m_M24 - p_Mat2.m_M24,
		p_Mat1.m_M31 - p_Mat2.m_M31, p_Mat1.m_M32 - p_Mat2.m_M32, p_Mat1.m_M33 - p_Mat2.m_M33, p_Mat1.m_M34 - p_Mat2.m_M34,
		p_Mat1.m_M41 - p_Mat2.m_M41, p_Mat1.m_M42 - p_Mat2.m_M42, p_Mat1.m_M43 - p_Mat2.m_M43, p_Mat1.m_M44 - p_Mat2.m_M44);
}

// scalar multiplication
inline const Matrix4 operator*(const Matrix4& p_Mat, const float p_Value)
{
	return Matrix4(p_Mat.m_M11 * p_Value, p_Mat.m_M12 * p_Value, p_Mat.m_M13 * p_Value, p_Mat.m_M14 * p_Value,
					p_Mat.m_M21 * p_Value, p_Mat.m_M22 * p_Value, p_Mat.m_M23 * p_Value, p_Mat.m_M24 * p_Value,
					p_Mat.m_M31 * p_Value, p_Mat.m_M32 * p_Value, p_Mat.m_M33 * p_Value, p_Mat.m_M34 * p_Value,
					p_Mat.m_M41 * p_Value, p_Mat.m_M42 * p_Value, p_Mat.m_M43 * p_Value, p_Mat.m_M44 * p_Value);
}

// scalar multiplication
inline const Matrix4 operator*(const float p_Value, const Matrix4& p_Mat)
{
	return Matrix4(p_Mat.m_M11 * p_Value, p_Mat.m_M12 * p_Value, p_Mat.m_M13 * p_Value, p_Mat.m_M14 * p_Value,
					p_Mat.m_M21 * p_Value, p_Mat.m_M22 * p_Value, p_Mat.m_M23 * p_Value, p_Mat.m_M24 * p_Value,
					p_Mat.m_M31 * p_Value, p_Mat.m_M32 * p_Value, p_Mat.m_M33 * p_Value, p_Mat.m_M34 * p_Value,
					p_Mat.m_M41 * p_Value, p_Mat.m_M42 * p_Value, p_Mat.m_M43 * p_Value, p_Mat.m_M44 * p_Value);
}

// matrix multiplication
inline const Matrix4 operator*(const Matrix4& p_Mat1,const Matrix4& p_Mat2)
{
	return Matrix4(
		// 1st row 
		p_Mat1.m_M11 * p_Mat2.m_M11 + p_Mat1.m_M12 * p_Mat2.m_M21 + p_Mat1.m_M13 * p_Mat2.m_M31 + p_Mat1.m_M14 * p_Mat2.m_M41,
		p_Mat1.m_M11 * p_Mat2.m_M12 + p_Mat1.m_M12 * p_Mat2.m_M22 + p_Mat1.m_M13 * p_Mat2.m_M32 + p_Mat1.m_M14 * p_Mat2.m_M42,
		p_Mat1.m_M11 * p_Mat2.m_M13 + p_Mat1.m_M12 * p_Mat2.m_M23 + p_Mat1.m_M13 * p_Mat2.m_M33 + p_Mat1.m_M14 * p_Mat2.m_M43,
		p_Mat1.m_M11 * p_Mat2.m_M14 + p_Mat1.m_M12 * p_Mat2.m_M24 + p_Mat1.m_M13 * p_Mat2.m_M34 + p_Mat1.m_M14 * p_Mat2.m_M44,
		// 2nd row
		p_Mat1.m_M21 * p_Mat2.m_M11 + p_Mat1.m_M22 * p_Mat2.m_M21 + p_Mat1.m_M23 * p_Mat2.m_M31 + p_Mat1.m_M24 * p_Mat2.m_M41,
		p_Mat1.m_M21 * p_Mat2.m_M12 + p_Mat1.m_M22 * p_Mat2.m_M22 + p_Mat1.m_M23 * p_Mat2.m_M32 + p_Mat1.m_M24 * p_Mat2.m_M42,
		p_Mat1.m_M21 * p_Mat2.m_M13 + p_Mat1.m_M22 * p_Mat2.m_M23 + p_Mat1.m_M23 * p_Mat2.m_M33 + p_Mat1.m_M24 * p_Mat2.m_M43,
		p_Mat1.m_M21 * p_Mat2.m_M14 + p_Mat1.m_M22 * p_Mat2.m_M24 + p_Mat1.m_M23 * p_Mat2.m_M34 + p_Mat1.m_M24 * p_Mat2.m_M44,
		// 3rd row
		p_Mat1.m_M31 * p_Mat2.m_M11 + p_Mat1.m_M32 * p_Mat2.m_M21 + p_Mat1.m_M33 * p_Mat2.m_M31 + p_Mat1.m_M34 * p_Mat2.m_M41,
		p_Mat1.m_M31 * p_Mat2.m_M12 + p_Mat1.m_M32 * p_Mat2.m_M22 + p_Mat1.m_M33 * p_Mat2.m_M32 + p_Mat1.m_M34 * p_Mat2.m_M42,
		p_Mat1.m_M31 * p_Mat2.m_M13 + p_Mat1.m_M32 * p_Mat2.m_M23 + p_Mat1.m_M33 * p_Mat2.m_M33 + p_Mat1.m_M34 * p_Mat2.m_M43,
		p_Mat1.m_M31 * p_Mat2.m_M14 + p_Mat1.m_M32 * p_Mat2.m_M24 + p_Mat1.m_M33 * p_Mat2.m_M34 + p_Mat1.m_M34 * p_Mat2.m_M44,
		// 4th row
		p_Mat1.m_M41 * p_Mat2.m_M11 + p_Mat1.m_M42 * p_Mat2.m_M21 + p_Mat1.m_M43 * p_Mat2.m_M31 + p_Mat1.m_M44 * p_Mat2.m_M41,
		p_Mat1.m_M41 * p_Mat2.m_M12 + p_Mat1.m_M42 * p_Mat2.m_M22 + p_Mat1.m_M43 * p_Mat2.m_M32 + p_Mat1.m_M44 * p_Mat2.m_M42,
		p_Mat1.m_M41 * p_Mat2.m_M13 + p_Mat1.m_M42 * p_Mat2.m_M23 + p_Mat1.m_M43 * p_Mat2.m_M33 + p_Mat1.m_M44 * p_Mat2.m_M43,
		p_Mat1.m_M41 * p_Mat2.m_M14 + p_Mat1.m_M42 * p_Mat2.m_M24 + p_Mat1.m_M43 * p_Mat2.m_M34 + p_Mat1.m_M44 * p_Mat2.m_M44
	);
}

// scalar division
inline const Matrix4 operator/(const Matrix4& p_Mat, const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	return Matrix4(p_Mat.m_M11 * invValue, p_Mat.m_M12 * invValue, p_Mat.m_M13 * invValue, p_Mat.m_M14 * invValue,
					p_Mat.m_M21 * invValue, p_Mat.m_M22 * invValue, p_Mat.m_M23 * invValue, p_Mat.m_M24 * invValue,
					p_Mat.m_M31 * invValue, p_Mat.m_M32 * invValue, p_Mat.m_M33 * invValue, p_Mat.m_M34 * invValue,
					p_Mat.m_M41 * invValue, p_Mat.m_M42 * invValue, p_Mat.m_M43 * invValue, p_Mat.m_M44 * invValue);
}

// scalar division
inline const Matrix4 operator/(const float p_Value, const Matrix4& p_Mat)
{
	const float invValue = 1.0f / p_Value;
	return Matrix4(p_Mat.m_M11 * invValue, p_Mat.m_M12 * invValue, p_Mat.m_M13 * invValue, p_Mat.m_M14 * invValue,
					p_Mat.m_M21 * invValue, p_Mat.m_M22 * invValue, p_Mat.m_M23 * invValue, p_Mat.m_M24 * invValue,
					p_Mat.m_M31 * invValue, p_Mat.m_M32 * invValue, p_Mat.m_M33 * invValue, p_Mat.m_M34 * invValue,
					p_Mat.m_M41 * invValue, p_Mat.m_M42 * invValue, p_Mat.m_M43 * invValue, p_Mat.m_M44 * invValue);
}


// mulitplication of (column) vector3 and matrix
inline Vector3 operator*=(Vector3& p_Vec, const Matrix4& p_Mat)
{
	return (p_Vec = p_Mat * p_Vec);
}

// mulitplication of (column) vector4 and matrix
inline Vector4 operator*=(Vector4& p_Vec, const Matrix4& p_Mat)
{
	return (p_Vec = p_Mat * p_Vec);
}

// multiplication of (column) vector3 with matrix
inline const Vector3 operator*(const Matrix4& p_Mat, const Vector3& p_Vec)
{
    return Vector3(p_Mat.m_M11 * p_Vec.m_X + p_Mat.m_M12 * p_Vec.m_Y + p_Mat.m_M13 * p_Vec.m_Z + p_Mat.m_M14,
					p_Mat.m_M21 * p_Vec.m_X + p_Mat.m_M22 * p_Vec.m_Y + p_Mat.m_M23 * p_Vec.m_Z + p_Mat.m_M24,
					p_Mat.m_M31 * p_Vec.m_X + p_Mat.m_M32 * p_Vec.m_Y + p_Mat.m_M33 * p_Vec.m_Z + p_Mat.m_M34);
}

// multiplication of (row) vector3 with matrix
inline const Vector3 operator*(const Vector3& p_Vec, const Matrix4& p_Mat)
{
	return Vector3(p_Mat.m_M11 * p_Vec.m_X + p_Mat.m_M21 * p_Vec.m_Y + p_Mat.m_M31 * p_Vec.m_Z + p_Mat.m_M41,
					p_Mat.m_M12 * p_Vec.m_X + p_Mat.m_M22 * p_Vec.m_Y + p_Mat.m_M32 * p_Vec.m_Z + p_Mat.m_M42,
					p_Mat.m_M13 * p_Vec.m_X + p_Mat.m_M23 * p_Vec.m_Y + p_Mat.m_M33 * p_Vec.m_Z + p_Mat.m_M43);
}

// multiplication of (column)vector4 with matrix
inline const Vector4 operator*(const Matrix4& p_Mat, const Vector4& p_Vec)
{
	return Vector4(p_Mat.m_M11 * p_Vec.m_X + p_Mat.m_M12 * p_Vec.m_Y + p_Mat.m_M13 * p_Vec.m_Z + p_Mat.m_M14 * p_Vec.m_W,
					p_Mat.m_M21 * p_Vec.m_X + p_Mat.m_M22 * p_Vec.m_Y + p_Mat.m_M23 * p_Vec.m_Z + p_Mat.m_M24 * p_Vec.m_W,
					p_Mat.m_M31 * p_Vec.m_X + p_Mat.m_M32 * p_Vec.m_Y + p_Mat.m_M33 * p_Vec.m_Z + p_Mat.m_M34 * p_Vec.m_W,
					p_Mat.m_M41 * p_Vec.m_X + p_Mat.m_M42 * p_Vec.m_Y + p_Mat.m_M43 * p_Vec.m_Z + p_Mat.m_M44 * p_Vec.m_W);
}

// multiplication of (row)vector4 with matrix
inline const Vector4 operator*(const Vector4& p_Vec, const Matrix4& p_Mat)
{
	return Vector4(p_Mat.m_M11 * p_Vec.m_X + p_Mat.m_M21 * p_Vec.m_Y + p_Mat.m_M31 * p_Vec.m_Z + p_Mat.m_M41 * p_Vec.m_W,
					p_Mat.m_M12 * p_Vec.m_X + p_Mat.m_M22 * p_Vec.m_Y + p_Mat.m_M32 * p_Vec.m_Z + p_Mat.m_M42 * p_Vec.m_W,
					p_Mat.m_M13 * p_Vec.m_X + p_Mat.m_M23 * p_Vec.m_Y + p_Mat.m_M33 * p_Vec.m_Z + p_Mat.m_M43 * p_Vec.m_W,
					p_Mat.m_M14 * p_Vec.m_X + p_Mat.m_M24 * p_Vec.m_Y + p_Mat.m_M34 * p_Vec.m_Z + p_Mat.m_M44 * p_Vec.m_W);
}

// assignment operator (float = assign to all values)
inline Matrix4& Matrix4::operator=(const float p_Value)
{
	m_M11 = p_Value;	m_M12 = p_Value;	m_M13 = p_Value;	m_M14 = p_Value;
	m_M21 = p_Value;	m_M22 = p_Value;	m_M23 = p_Value;	m_M24 = p_Value;
	m_M31 = p_Value;	m_M32 = p_Value;	m_M33 = p_Value;	m_M34 = p_Value;
	m_M41 = p_Value;	m_M42 = p_Value;	m_M43 = p_Value;	m_M44 = p_Value;

	return (*this);
}

inline Matrix4& Matrix4::operator=(const Matrix4& p_Mat)
{
	m_M11 = p_Mat.m_M11;	m_M12 = p_Mat.m_M12;	m_M13 = p_Mat.m_M13;	m_M14 = p_Mat.m_M14;
	m_M21 = p_Mat.m_M21;	m_M22 = p_Mat.m_M22;	m_M23 = p_Mat.m_M23;	m_M24 = p_Mat.m_M24;
	m_M31 = p_Mat.m_M31;	m_M32 = p_Mat.m_M32;	m_M33 = p_Mat.m_M33;	m_M34 = p_Mat.m_M34;
	m_M41 = p_Mat.m_M41;	m_M42 = p_Mat.m_M42;	m_M43 = p_Mat.m_M43;	m_M44 = p_Mat.m_M44;

	return (*this);
}

// make identity matrix
inline void Matrix4::Identity()
{
	m_M11 = 1.0f;	m_M12 = 0.0f;	m_M13 = 0.0f;	m_M14 = 0.0f;
	m_M21 = 0.0f;	m_M22 = 1.0f;	m_M23 = 0.0f;	m_M24 = 0.0f;
	m_M31 = 0.0f;	m_M32 = 0.0f;	m_M33 = 1.0f;	m_M34 = 0.0f;
	m_M41 = 0.0f;	m_M42 = 0.0f;	m_M43 = 0.0f;	m_M44 = 1.0f;   
}

// only applys the matrix's rotation info to the (column) vector
inline const Vector3 Matrix4::RotateOnly(const Vector3& p_Vec)
{
	return Vector3(m_M11 * p_Vec.m_X + m_M12 * p_Vec.m_Y + m_M13 * p_Vec.m_Z,
					m_M21 * p_Vec.m_X + m_M22 * p_Vec.m_Y + m_M23 * p_Vec.m_Z,
					m_M31 * p_Vec.m_X + m_M32 * p_Vec.m_Y + m_M33 * p_Vec.m_Z);
}

// set/get a matrix column
inline const Vector4 Matrix4::GetCol(uint32 p_Col)
{
#ifdef USE_PRETRANSPOSED_MATRIX
	return Vector4(m_MatE[p_Col][0], m_MatE[p_Col][1], m_MatE[p_Col][2], m_MatE[p_Col][3]);
#else
	return Vector4(m_MatE[0][p_Col], m_MatE[1][p_Col], m_MatE[2][p_Col], m_MatE[3][p_Col]);
#endif
}

inline void	Matrix4::SetCol(uint32 p_Col, const Vector4& p_Vec)
{
#ifdef USE_PRETRANSPOSED_MATRIX
	m_MatE[p_Col][0] = p_Vec.m_X;
	m_MatE[p_Col][1] = p_Vec.m_Y;
	m_MatE[p_Col][2] = p_Vec.m_Z;
	m_MatE[p_Col][3] = p_Vec.m_W;
#else
	m_MatE[0][p_Col] = p_Vec.m_X;
	m_MatE[1][p_Col] = p_Vec.m_Y;
	m_MatE[2][p_Col] = p_Vec.m_Z;
	m_MatE[3][p_Col] = p_Vec.m_W;
#endif
}

inline const Vector4 Matrix4::GetRow(uint32 p_Row)
{
#ifdef USE_PRETRANSPOSED_MATRIX
	return Vector4(m_MatE[0][p_Row], m_MatE[1][p_Row], m_MatE[2][p_Row], m_MatE[3][p_Row]);
#else
	return Vector4(m_MatE[p_Row][0], m_MatE[p_Row][1], m_MatE[p_Row][2], m_MatE[p_Row][3]);
#endif
}

inline void Matrix4::SetRow(uint32 p_Row, const Vector4& p_Vec)
{
#ifdef USE_PRETRANSPOSED_MATRIX
	m_MatE[0][p_Row] = p_Vec.m_X;
	m_MatE[1][p_Row] = p_Vec.m_Y;
	m_MatE[2][p_Row] = p_Vec.m_Z;
	m_MatE[3][p_Row] = p_Vec.m_W;
#else
	m_MatE[p_Row][0] = p_Vec.m_X;
	m_MatE[p_Row][1] = p_Vec.m_Y;
	m_MatE[p_Row][2] = p_Vec.m_Z;
	m_MatE[p_Row][3] = p_Vec.m_W;
#endif
}

// creates a perspective projection matrix
inline void Matrix4::PerspectiveMatrix(const float p_Fov, const float p_Aspect, const float p_Near, const float p_Far)
{
	const float f = Math::Cot(p_Fov / 2.0f);
	const float invDelta = 1.0f / (p_Far - p_Near);

	m_M11 = f / p_Aspect;	m_M12 = 0.0f;	m_M13 = 0.0f;				m_M14 = 0.0f;
	m_M21 = 0.0f;			m_M22 = f;		m_M23 = 0.0f;				m_M24 = 0.0f;
	m_M31 = 0.0f;			m_M32 = 0.0f;	m_M33 = p_Far * invDelta;	m_M34 = (-p_Near * p_Far) * invDelta;
	m_M41 = 0.0f;			m_M42 = 0.0f;	m_M43 = 1.0f;				m_M44 = 0.0f;   
}	

// create an orthogonal projection matrix
inline void Matrix4::OrthoMatrix(const float p_Left, const float p_Top, const float p_Right, const float p_Bottom, 
								  const float p_Near, const float p_Far)
{
	m_M11 = 2.0f / (p_Right - p_Left);	m_M12 = 0.0f;						m_M13 = 0.0f;						m_M14 = (p_Left + p_Right) / (p_Left - p_Right);
	m_M21 = 0.0f;						m_M22 = 2.0f / (p_Top - p_Bottom);	m_M23 = 0.0f;						m_M24 = (p_Top + p_Bottom) / (p_Bottom - p_Top);
	m_M31 = 0.0f;						m_M32 = 0.0f;						m_M33 = 1.0f / (p_Far - p_Near);	m_M34 = p_Near / (p_Near - p_Far);
	m_M41 = 0.0f;						m_M42 = 0.0f;						m_M43 = 0.0f;						m_M44 = 1.0f;   
}


} // namespace Math

#endif // MATRIX4_H
